//
//  JCUnwrapUtil.swift
//  JCPublicKit
//
//  Created by JasonLee on 2020/5/12.
//

import Foundation

/**
 Unwrap util,
 contains methods that will return specific type value/object
 with index from a array or with key from a dictionary,
 and methods to convert value to specific type value/object.
 Get value from dictionary/array safeter.
 */
// MARK: - Converter
class JCUnwrapUtil: NSObject {
    /**
     * Convert value to String.
     * If value's type is in (Int, Float, Double, Bool) will also try converting.
     * - parameter value: data source.
     * - returns: Will return NIL while converting failed.
     */
    static func string(from value: Any?) -> String? {
        if let stringValue = value as? String {
            return stringValue
        } else if let intValue = value as? Int {
            return "\(intValue)"
        } else if let floatValue = value as? Float {
            return "\(floatValue)"
        } else if let doubleValue = value as? Double {
            return "\(doubleValue)"
        } else if let boolValue = value as? Bool {
            return "\(boolValue)"
        }
        return nil
    }
    
    /**
     * Convert value to Int.
     * If value's type is String will also try converting.
     * - parameter value: data source.
     * - returns: Will return NIL while converting failed.
     */
    static func int(from value: Any?) -> Int? {
        if let intValue = value as? Int {
            return intValue
        } else if let stringValue = value as? String {
            return Int(stringValue)
        }
        return nil
    }

    /**
     * Convert value to Float.
     * If value's type is String will also try converting.
     * - parameter value: data source.
     * - returns: Will return nil while converting failed.
     */
    static func float(from value: Any?) -> Float? {
        if let floatValue = value as? Float {
            return floatValue
        } else if let stringValue = value as? String {
            return Float(stringValue)
        }
        return nil
    }

    /**
    * Convert value to Float.
    * If value's type is String will also try converting.
    * - parameter value: data source.
    * - returns: Will return nil while converting failed.
    */
    static func double(from value: Any?) -> Double? {
        if let stringValue = value as? String {
            return Double(stringValue)
        } else if let doubleValue = value as? Double {
            return doubleValue
        }
        return nil
    }

    /**
    * Convert value to Float.
    * If value's type is String will also try converting.
    * - parameter value: data source.
    * - returns: Will return nil while converting failed.
    */
    static func bool(from value: Any?) -> Bool? {
        if let stringValue = value as? String {
            return Bool(stringValue)
        } else if let boolValue = value as? Bool {
            return boolValue
        }
        return nil
    }
}

// MARK: - FOR DICTIONARY
extension JCUnwrapUtil {
    // MARK: RETURN ANY VALUE FOR KEY
    /**
     Return an Any value for key.
     Objective-C available.
     - parameter dictionary: Optional datasource.
     - parameter key
     - returns: Will return nil while no value exist for key.
     */
    @objc static func value(from dictionary: [String : Any]?, for key: String) -> Any? {
        guard let dict = dictionary as NSDictionary? else { return nil }
        return dict[key]
    }
    
    // MARK: RETURN STRING VALUE FOR KEY
    /**
     Return a string value for key.
     Objective-C available.
     - parameter dictionary: Optional datasource.
     - parameter key
     - returns: Will return nil while no value exist for key or value is not a String value.
     */
    @objc static func string(from dictionary: [String : Any]?, for key: String) -> String? {
        guard
            let value = value(from: dictionary, for: key),
            let string = string(from: value) else { return nil }
        return string
    }
    
    // MARK: RETURN INT VALUE FOR KEY
    /**
     Return an Int value for key.
     - parameter dictionary: Optional datasource.
     - parameter key
     - returns: Will return nil while no value exist for key or value is not a Int value.
     */
    static func int(from dictionary: [String : Any]?, for key: String) -> Int? {
        guard
            let value = value(from: dictionary, for: key),
            let int = int(from: value) else { return nil }
        return int
    }
    
    // MARK: RETURN DOUBLE VALUE FOR KEY
    /**
     Return a Double value for key.
     - parameter dictionary: Optional datasource.
     - parameter key
     - returns: Will return nil while no value exist for key or value is not a Double value.
     */
    static func double(from dictionary: [String : Any]?, for key: String) -> Double? {
        guard
            let value = value(from: dictionary, for: key),
            let double = double(from: value) else { return nil }
        return double
    }
    
    // MARK: RETURN BOOL VALUE FOR KEY
    /**
     Return a Bool value for key.
     - parameter dictionary: Optional datasource.
     - parameter key
     - returns: Will return nil while no value exist for key or value is not a Bool value.
     */
    static func bool(from dictionary: [String : Any]?, for key: String) -> Bool? {
        guard
            let value = value(from: dictionary, for: key),
            let bool = bool(from: value) else { return nil }
        return bool
    }
    
    // MARK: RETURN ARRAY VALUE FOR KEY
    /**
     Return a generic Array<T> value for key.
     - parameter dictionary: Optional datasource.
     - parameter key
     - returns: Will return nil while no value exist for key or value is not a Array<T> value.
     */
    static func array<T>(from dictionary: [String : Any]?, for key: String) -> [T]? {
        if let arrayObj = value(from: dictionary, for: key) as? [T] {
            return arrayObj
        }
        return nil
    }
    /**
     Return a common Array<Any> value for key.
     Objective-C available.
     - parameter dictionary: Optional datasource.
     - parameter key
     - returns: Will return nil while no value exist for key or value is not a Array<Any> value.
     */
    @objc static func array(from dictionary: [String : Any]?, for key: String) -> [Any]? {
        if let arrayObj = value(from: dictionary, for: key) as? [Any] {
            return arrayObj
        }
        return nil
    }
    
    // MARK: RETURN DICTIONARY VALUE FOR KEY
    /**
     Return a generic Dictionary<K, V> value for key.
     - parameter dictionary: Optional datasource.
     - parameter key
     - returns: Will return nil while no value exist for key or value is not a Dictionary<K, V> value.
     */
    static func dictionary<K, V>(from dictionary: [String : Any]?, for key: String) -> Dictionary<K, V>? {
        if let dictObj = value(from: dictionary, for: key) as? [K : V] {
            return dictObj
        }
        return nil
    }
    /**
     Return a common Dictionary<String, Any> value for key.
     Objective-C available.
     - parameter dictionary: Optional datasource.
     - parameter key
     - returns: Will return nil while no value exist for key or value is not a Dictionary<String, Any> value.
     */
    @objc static func dictionary(from dictionary: [String : Any]?, for key: String) -> [String : Any]? {
        if let dictObj = value(from: dictionary, for: key) as? [String : Any] {
            return dictObj
        }
        return nil
    }
}

// MARK: - Array
extension JCUnwrapUtil {
    // MARK: RETURN ANY AT INDEX
    /**
    Return a Any value at index.
    Objective-C available.
    - parameter array: Optional datasource.
    - parameter index
    - returns: Will return nil while no value exist at index.
    */
    @objc static func value(from array: [Any]?, at index: Int) -> Any? {
        guard let array = array, !JCUtil.isIndex(index, outOfBounds: array) else { return nil }
        return array[index]
    }
    
    // MARK: RETURN STRING AT INDEX
    /**
    Return a String value at index.
    Objective-C available.
    - parameter array: Optional datasource.
    - parameter index
    - returns: Will return nil while value at index is not a String value or no value exist at index.
    */
    @objc static func string(from array: [Any]?, at index: Int) -> String? {
        guard
            let value = value(from: array, at: index),
            let any = string(from: value) else { return nil }
        return any
    }
    
    // MARK: RETURN INT AT INDEX
    /**
    Return a Int value at index.
    - parameter array: Optional datasource.
    - parameter index
    - returns: Will return nil while value at index is not a Int value or no value exist at index.
    */
    static func int(from array: [Any]?, at index: Int) -> Int? {
        guard
            let value = value(from: array, at: index),
            let any = int(from: value) else { return nil }
        return any
    }
    
    // MARK: RETURN DOUBLE AT INDEX
    /**
    Return a Double value at index.
    - parameter array: Optional datasource.
    - parameter index
    - returns: Will return nil while value at index is not a Double value or no value exist at index.
    */
    static func double(from array: [Any]?, at index: Int) -> Double? {
        guard
            let value = value(from: array, at: index),
            let any = double(from: value) else { return nil }
        return any
    }
    
    // MARK: RETURN BOOL AT INDEX
    /**
    Return a bool value at index.
    - parameter array: Optional datasource.
    - parameter index
    - returns: Will return nil while value at index is not a Bool value or no value exist at index.
    */
    static func bool(from array: [Any]?, at index: Int) -> Bool? {
        guard
            let value = value(from: array, at: index),
            let any = bool(from: value) else { return nil }
        return any
    }
    
    // MARK: RETURN ARRAY AT INDEX
    /**
    Return a generic Array<T> value at index.
    - parameter array: Optional datasource.
    - parameter index
    - returns: Will return nil while value at index is not a Array<T> value or no value exist at index.
    */
    static func array<T>(from array: [Any]?, at index: Int) -> [T]? {
        guard let value = value(from: array, at: index) else { return nil }
        return value as? [T]
    }
    /**
    Return a common Array<Any> value at index.
    Objective-C available.
    - parameter array: Optional datasource.
    - parameter index
    - returns: Will return nil while value at index is not a Array<Any> value or no value exist at index..
    */
    @objc static func array(from array: [Any]?, at index: Int) -> [Any]? {
        guard let value = value(from: array, at: index) else { return nil }
        return value as? [Any]
    }
    
    // MARK: RETURN DICTIONARY AT INDEX
    /**
    Return a generic Dictionary<K, V> value at index.
    - parameter array: Optional datasource.
    - parameter index
    - returns: Will return nil while value at index is not a Dictionary<K, V>  value or no value exist at index..
    */
    static func dictionary<K, V>(from array: [Any]?, at index: Int) -> Dictionary<K, V>? {
        guard let value = value(from: array, at: index) else { return nil }
        return value as? [K : V]
    }
    /**
    Return a common Dictionary<String, Any> value at index.
    - parameter array: Optional datasource.
    - parameter index
    - returns: Will return nil while value at index is not a Dictionary<String, Any> value or no value exist at index..
    */
    @objc static func dictionary(from array: [Any]?, at index: Int) -> [String : Any]? {
        guard let value = value(from: array, at: index) else { return nil }
        return value as? [String : Any]
    }
}

// MARK: - EXTENSION
extension Dictionary where Key == String, Value: Any {
    // MARK: RETURN ANY VALUE FOR KEY
    /**
    Return an Any value for key.
    - parameter key
    - returns: Will return nil while no value exist for key.
    */
    func jc_value(for key: String) -> Any? {
        return JCUnwrapUtil.value(from: self, for: key)
    }
    // MARK: RETURN STRING VALUE FOR KEY
    /**
     Return a string value for key.
     - parameter key
     - returns: Will return nil while no value exist for key or value is not a String value.
     */
    func jc_string(for key: String) -> String? {
        return JCUnwrapUtil.string(from: self, for: key)
    }
    // MARK: RETURN INT VALUE FOR KEY
    /**
     Return an Int value for key.
     - parameter key
     - returns: Will return nil while no value exist for key or value is not a Int value.
     */
    func jc_int(for key: String) -> Int? {
        return JCUnwrapUtil.int(from: self, for: key)
    }
    // MARK: RETURN DOUBLE VALUE FOR KEY
    /**
     Return a Double value for key.
     - parameter key
     - returns: Will return nil while no value exist for key or value is not a Double value.
     */
    func jc_double(for key: String) -> Double? {
        return JCUnwrapUtil.double(from: self, for: key)
    }
    // MARK: RETURN BOOL VALUE FOR KEY
    /**
     Return a Bool value for key.
     - parameter key
     - returns: Will return nil while no value exist for key or value is not a Bool value.
     */
    func jc_bool(for key: String) -> Bool? {
        return JCUnwrapUtil.bool(from: self, for: key)
    }
    // MARK: RETURN ARRAY VALUE FOR KEY
    /**
     Return a generic Array<T> value for key.
     - parameter dictionary: Optional datasource.
     - parameter key
     - returns: Will return nil while no value exist for key or value is not a Array<T> value.
     */
    func jc_array<T>(for key: String) -> [T]? {
        return JCUnwrapUtil.array(from: self, for: key)
    }
    // MARK: RETURN DICTIONARY VALUE FOR KEY
    /**
     Return a generic Dictionary<K, V> value for key.
     - parameter dictionary: Optional datasource.
     - parameter key
     - returns: Will return nil while no value exist for key or value is not a Dictionary<K, V> value.
     */
    func jc_dictionary<K, V>(for key: String) -> [K : V]? {
        return JCUnwrapUtil.dictionary(from: self, for: key)
    }
    // MARK: RETURN GENERIC VALUE FOR KEY
    /**
     Return a generic value for key.
     - parameter key
     - returns: Will return nil while value at index converting failed or no value exist for key.
     */
    func jc_get<T>(for key: String) -> T? {
        return JCUnwrapUtil.value(from: self, for: key) as? T
    }
}
// MARK: 数组部分
extension Array {
    // MARK: RETURN ANY AT INDEX
    /**
    Return a Any value at index.
    - parameter index
    - returns: Will return nil while no value exist at index.
    */
    func jc_value(at index: Int) -> Element? {
        return JCUnwrapUtil.value(from: self, at: index) as? Element
    }
    // MARK: RETURN STRING AT INDEX
    /**
    Return a String value at index.
    - parameter index
    - returns: Will return nil while value at index is not a String value or no value exist at index.
    */
    func jc_string(at index: Int) -> String? {
        return JCUnwrapUtil.string(from: self, at: index)
    }
    // MARK: RETURN INT AT INDEX
    /**
    Return a Int value at index.
    - parameter index
    - returns: Will return nil while value at index is not a Int value or no value exist at index.
    */
    func jc_int(at index: Int) -> Int? {
        return JCUnwrapUtil.int(from: self, at: index)
    }
    // MARK: RETURN DOUBLE AT INDEX
    /**
    Return a Double value at index.
    - parameter index
    - returns: Will return nil while value at index is not a Double value or no value exist at index.
    */
    func jc_double(at index: Int) -> Double? {
        return JCUnwrapUtil.double(from: self, at: index)
    }
    // MARK: RETURN BOOL AT INDEX
    /**
    Return a bool value at index.
    - parameter index
    - returns: Will return nil while value at index is not a Bool value or no value exist at index.
    */
    func jc_bool(at index: Int) -> Bool? {
        return JCUnwrapUtil.bool(from: self, at: index)
    }
    // MARK: RETURN ARRAY AT INDEX
    /**
    Return a generic Array<T> value at index.
    - parameter index
    - returns: Will return nil while value at index is not a Array<T> value or no value exist at index.
    */
    func jc_array<T>(at index: Int) -> [T]? {
        return JCUnwrapUtil.array(from: self, at: index)
    }
    // MARK: RETURN DICTIONARY AT INDEX
    /**
    Return a generic Dictionary<K, V> value at index.
    - parameter index
    - returns: Will return nil while value at index is not a Dictionary<K, V>  value or no value exist at index..
    */
    func jc_dictionary<K, V>(at index: Int) -> [K : V]? {
        return JCUnwrapUtil.dictionary(from: self, at: index)
    }
    // MARK: RETURN GENERIC VALUE AT INDEX
    /**
    Return a generic value at index.
     - parameter index
     - returns: Will return nil while value at index converting failed or no value exist at index..
     */
    func jc_get<T>(at index: Int) -> T? {
        return JCUnwrapUtil.value(from: self, at: index) as? T
    }
}
